//! @file   lc_file.h
//! @brief  
//! @author Sayan Chaliha
//! @date   September, 2011
//!
//! 
#include "libcyan_internal.h"
#include "lc_types.h"
#include "lc_forward_decl.h"
#include "lc_basic_string.h"
#include "lc_multi_reader_lock.h"
#include "lc_object.h"

namespace cyan {
  class CYAN_API File : public Object {
  public:
    enum Type {
      TYPE_DIRECTORY,
      TYPE_REGULAR,
      TYPE_CHARACTER_SPECIAL,
      TYPE_OTHER
    };

    enum Whence {
      WHENCE_SET = SEEK_SET,
      WHENCE_CURRENT = SEEK_CUR,
      WHENCE_END = SEEK_END
    };

    enum Mode {
      MODE_READ_USER = FILE_MODE_READ_USER,
      MODE_WRITE_USER = FILE_MODE_WRITE_USER,
      MODE_EXEC_USER = FILE_MODE_EXEC_USER,
      MODE_RWX_USER = FILE_MODE_RWX_USER,

      MODE_READ_GROUP = FILE_MODE_READ_GROUP,
      MODE_WRITE_GROUP = FILE_MODE_WRITE_GROUP,
      MODE_EXEC_GROUP = FILE_MODE_EXEC_GROUP,
      MODE_RWX_GROUP = FILE_MODE_RWX_GROUP,

      MODE_READ_OTHER = FILE_MODE_READ_OTHER,
      MODE_WRITE_OTHER = FILE_MODE_WRITE_OTHER,
      MODE_EXEC_OTHER = FILE_MODE_EXEC_OTHER,
      MODE_RWX_OTHER = FILE_MODE_RWX_OTHER,

      MODE_DEFAULT = FILE_MODE_DEFAULT
    };

    enum Check {
      CHECK_EXISTS = FILE_ACCESS_EXISTS,
      CHECK_READ_OK = FILE_ACCESS_READ_OK,
      CHECK_WRITE_OK = FILE_ACCESS_WRITE_OK,
      CHECK_RW_OK = FILE_ACCESS_RW_OK,
      CHECK_EXEC_OK = FILE_ACCESS_EXEC_OK
    };

    enum Flag {
      FLAG_READ = FILE_OPEN_READONLY,
      FLAG_WRITE = FILE_OPEN_WRITEONLY,
      FLAG_READ_WRITE = FILE_OPEN_READWRITE,
      FLAG_NONBLOCKING = FILE_OPEN_NONBLOCKING,
      FLAG_ASYNCHRONOUS = FILE_OPEN_ASYNCHRONOUS,
      FLAG_NO_FOLLOW = FILE_OPEN_NO_FOLLOW,
      FLAG_APPEND = FILE_OPEN_APPEND,
      FLAG_CREATE = FILE_OPEN_CREATE,
      FLAG_TRUNCATE = FILE_OPEN_TRUNCATE,
      FLAG_CREATE_EXCLUSIVE = FILE_OPEN_CREATE_EXCL,
      FLAG_NO_CTTY = FILE_OPEN_NO_CTTY,
      FLAG_SYNCHRONOUS = FILE_OPEN_SYNCHRONOUS,
      FLAG_BINARY = FILE_OPEN_BINARY,
      FLAG_TEXT = FILE_OPEN_TEXT,
      FLAG_DEFAULT = FILE_OPEN_DEFAULT
    };

    enum ReservedPosition {
      POSITION_BEGIN,
      POSITION_CURRENT,
      POSITION_END
    };

    enum Lock {
      LOCK_READ,
      LOCK_WRITE,
      LOCK_NONE
    };

    class CYAN_API Info : public Object {
    public:
      Info(const Info& fileInfo);
      Info(const FileStatus& fileStatus);
      virtual ~Info();

      Int     fileDeviceId()          const;
      Time    fileAccessTime()        const;
      Time    fileModificationTime()  const;
      Time    fileCreationTime()      const;
      Offset  fileSize()              const;
      Int64   fileBlockCount()        const;
      Int     fileOptimumBlockSize()  const;
      Type    fileType()              const;

    private:
      Info();

      Int CYAN_HIDDEN updateInfo(const String& filePath);
      Int CYAN_HIDDEN updateInfo(Int fileDescriptor);

      FileStatus statBuffer_;

      friend class File;
    };

    File(const String& filePath, Int flags = FLAG_DEFAULT,
        Int mode = MODE_DEFAULT);
    File(const File&);
    virtual ~File();

    virtual void    open(const String& filePath, Int flags = FLAG_DEFAULT,
        Int mode = MODE_DEFAULT);
    void            close();
    Offset          seek(Offset from, Whence whence);
    Offset          seek(ReservedPosition position);
    Offset          tell()                                    const;
    SSize           read(void* buffer, Size bytes);
    SSize           write(void* buffer, Size bytes);
    Bool            isOpen()                                  const;
    Info            info()                                    const;
    Bool            access(UInt16 checkFlags = CHECK_EXISTS)  const;
    void            chmod(UInt16 modeFlags = MODE_DEFAULT)    const;
    void            remove();

    static File create(const String& filePath, Bool overwrite = false,
        UInt16 mode = MODE_DEFAULT);
    static void remove(const String& filePath);
    static Info info(const String& filePath);
    static Bool access(const String& filePath,
        UInt16 checkFlags = CHECK_EXISTS);
    static void chmod(const String& filePath, UInt16 mode = MODE_DEFAULT);
    static File createTemporary();

    template <typename T>
    File& operator <<(const T& object) {
      void* data = const_cast<void*>(reinterpret_cast<const void*>(&object));
      this->write(data, sizeof (object));
      return *this;
    }

    template <typename T>
    File& operator >>(T& object) {
      void* data = reinterpret_cast<void*>(&object);
      this->read(data, sizeof (object));
      return *this;
    }

  private:
    File();
    static void CYAN_HIDDEN testThreadCancellation();

    String        fileName_;
    Int           flags_;
    Int           mode_;
    Int           fileDescriptor_;
    Bool          isOpen_;
    mutable Bool  isTemporary_;
  };
}

